#include "MethodDEClassical.h"
#include "Problem.h"
#include "problem1.h"
#include <stdio.h>
#include <string.h>

#if TEST_VARIABLES==1
#include "engine.h"
#endif

MethodDEClassical::MethodDEClassical(Method::SubType subType, Problem * problem, unsigned int popSize, unsigned int iterations) :
    Method(subType, problem, popSize, iterations)
{
    setCr(0.5*(getProblem()->getDimention()));
}

MethodDEClassical::~MethodDEClassical() {}

OptimizedValue MethodDEClassical::optimize(int caseProblem)
{
#if TEST_VARIABLES==1
    Engine *ep;
    if (!(ep = engOpen("\0")))
    {
        fprintf(stderr, "\nCan't start MATLAB engine\n");
    }
    fprintf(stdout, "     *****     Sending data to MATLAB\n");
    mxArray *A;
    mxArray *B;
    A=mxCreateDoubleMatrix(getIterations(),1,mxREAL);
    B=mxCreateDoubleMatrix(getPopulationSize(),getIterations(),mxREAL);
    double * ptrA=mxGetPr(A);
    ptrA[0]=0.0;
    double * ptrB=mxGetPr(B);
    ptrB[0]=0.0;
#endif

    getProblem()->setFunction(caseProblem);

    OptimizedValue optValue;


    int dim=getProblem()->getDimention();
    unsigned int i=0 ,j=0, k=0;

    initialize();

    mutatedPopulation=parentPopulation;
    crossoverPopulation=parentPopulation;

    double bestFoundValue=(double)*min_element(cost.begin(), cost.end());
    bestFittedIndex=std::find(cost.begin(), cost.end(), (double) *min_element(cost.begin(), cost.end())) - cost.begin();
    std::vector<double> bestFoundAt=parentPopulation[bestFittedIndex];

    r.Reseed();
    double mutCost=0, crossoverCost=0, mutatedValue=0, val=0;

    for (i=0; i<getIterations(); i++)
    {
        bestFittedIndex=std::find(cost.begin(), cost.end(), (double) *min_element(cost.begin(), cost.end())) - cost.begin();

        for (j=0; j<populationSize; j++ )
        {
            mutation(j, k, mutCost, mutatedValue);
            crossover(j, k, crossoverCost);
            selecton(j, mutCost, crossoverCost);
        }
        mutatedPopulation=parentPopulation;
        crossoverPopulation=parentPopulation;

#if TEST_VARIABLES==1
        ptrA[i]=*min_element(cost.begin(), cost.end());
        for(j=0; j<populationSize; j++ )
            ptrB[i*populationSize+j]=cost[j];
#endif

    }
    optValue.value=(double) *min_element(cost.begin(), cost.end());
    optValue.atPoint=parentPopulation[std::find(cost.begin(), cost.end(), (double) *min_element(cost.begin(), cost.end())) - cost.begin()];



#if TEST_VARIABLES==1
    engPutVariable(ep,"bestFitDem",A);
    mxDestroyArray(A);
    engPutVariable(ep,"costArrayDem",B);
    mxDestroyArray(B);
    engEvalString(ep,"save('matlab/workspaceDemooooooo')");
    engClose(ep);
#endif

    std::cout<<optValue.value<<"\n";
    return optValue;
}




inline void MethodDEClassical::mutation(const unsigned int & j, unsigned int & k, double & mutCost, double & mutatedValue)
{
    unsigned int a=0, b=0, c=0;

    if(j!=bestFittedIndex)
    {
        do
            a=r.IntegerC(1,populationSize)-1;
        while(j==a);

        do
            b=r.IntegerC(1,populationSize)-1;
        while(b==j || b==a);

        do
            c=r.IntegerC(1,populationSize)-1;
        while(c==j || c==a || c==b);

        for(k=0; k<getProblem()->getDimention(); k++)
        {
            mutatedValue=parentPopulation[c][k]+2*(parentPopulation[a][k]-parentPopulation[b][k]);
            if((getProblem()->getCoordinates()[k].getUpperBound()>=mutatedValue)&&(getProblem()->getCoordinates()[k].getLowerBound()<=mutatedValue))
                mutatedPopulation[j][k]=mutatedValue;//or do modulo
        }
        mutCost=getProblem()->computeValue(mutatedPopulation[j]);
    }
}




inline void MethodDEClassical::crossover(const unsigned int & j, unsigned int & k, double & crossoverCost)
{
    double val;
    if(j!=bestFittedIndex)
        for(k=0; k<getProblem()->getDimention(); k++)
        {
            val=r.FloatN()*(getProblem()->getDimention());
            if(val<cr+0.2)
                crossoverPopulation[j][k]=mutatedPopulation[j][k];
        }
    crossoverCost=getProblem()->computeValue(crossoverPopulation[j]);
}




inline void MethodDEClassical::selecton(const unsigned int & j, double & mutCost,double & crossoverCost)
{

    if(mutCost>crossoverCost && cost[j]>crossoverCost)
    {
        parentPopulation[j]=crossoverPopulation[j];
        cost[j]=crossoverCost;

    }
    else if(mutCost<cost[j])
    {
        parentPopulation[j]=mutatedPopulation[j];
        cost[j]=mutCost;
    }
}
